package com.wuze.sprintboot.netty.handler;
import com.wuze.sprintboot.netty.entity.Path;
import com.wuze.sprintboot.netty.http.NettyHttpRequest;
import com.wuze.sprintboot.netty.http.NettyHttpResponse;
import com.wuze.sprintboot.netty.http.RequestParser;
import com.wuze.sprintboot.netty.util.AppUtil;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@ChannelHandler.Sharable
@Slf4j
public class HttpServerHandler extends ChannelInboundHandlerAdapter {

    private static final HashMap<String, Method> Path_Method_Map = AppUtil.getPath_Method_Map();

    private static final HashMap<String, Object> Path_Bean_Map = AppUtil.getPath_Bean_Map();


    private ExecutorService executor = Executors.newCachedThreadPool(runnable -> {
        Thread thread = Executors.defaultThreadFactory().newThread(runnable);
        thread.setName("NettyHttpHandler-" + thread.getName());
        return thread;
    });


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        NettyHttpRequest httpRequest = new NettyHttpRequest((FullHttpRequest) msg);
        Path path = http_To_Path(httpRequest);
        executor.execute(()->{
            FullHttpResponse Response = null;
            try {
                Response = handlerHttpRequest(path, httpRequest);
            } catch (Exception e) {
                e.printStackTrace();
                Response = NettyHttpResponse.makeError(e);
            }
            ctx.channel().writeAndFlush(Response).addListener(ChannelFutureListener.CLOSE);
            ReferenceCountUtil.release(msg);
        });
    }


    /**
     * 将http路径封装成Path
     * @param httpRequest
     * @return
     */
    public Path http_To_Path(NettyHttpRequest httpRequest){
        String[] split = httpRequest.getUri().split("\\?");
        return new Path(split[0], httpRequest.method().toString());
    }

    /**
     * 处理请求映射业务
     * @param path
     * @param request
     * @return
     * @throws Exception
     */
    public FullHttpResponse handlerHttpRequest(Path path , NettyHttpRequest request) throws Exception{

        Method method = Path_Method_Map.get(path.toString());
        Object bean = Path_Bean_Map.get(path.toString());
        //解析请求参数
        RequestParser requestParser = new RequestParser(request.getRealRequest());
        Map<String, String> parse = requestParser.parse();
        log.info("http:  {}",path.toString());
        if(method == null){
            log.error("404请求路径找不到：  {}",path.toString());
            return NettyHttpResponse.make(HttpResponseStatus.NOT_FOUND);
        }
        //获取参数数组
        Parameter[] parameters = method.getParameters();
        //设置objects数组
        Object[] objects = new Object[parameters.length];
        //给objects赋值
        for (int i = 0;i<parameters.length;i++) {
            //根据参数名字查找值
            String value = parse.get(parameters[i].getName());
            if(value != null){
                //非空则判断是否是基本类型
                objects[i] = String_TO_Object(value , parameters[i].getType());
            }else{
                //空则判断复杂对象类型
                //查找属性是否名字重合
                objects[i] = parameters[i].getType().newInstance();
                Field[] fields = parameters[i].getType().getDeclaredFields();
                for (Field field : fields) {
                    String v2 = parse.get(field.getName());
                    if(v2 != null){
                        Object object = String_TO_Object(v2, field.getType());

                        field.setAccessible(true);//私有属性需要强行赋值
                        field.set(objects[i] , object);
                    }
                }
            }
        }
        Object res = method.invoke(bean, objects);
        return NettyHttpResponse.ok(res.toString());
    }

    /**
     * 将字符串转换成Java基本数据类型及封装类
     * @param value 字符串
     * @param clazz 数据类型
     * @return Object
     */
    public Object String_TO_Object(String value , Class clazz){

        Object object = new Object();

        switch (clazz.toString()){
            case "byte":
                object = Byte.parseByte(value);
                break;
            case "short":
                object = Short.parseShort(value);
                break;
            case "int":
                object = Integer.parseInt(value);
                break;
            case "long":
                object = Long.parseLong(value);
                break;
            case "float":
                object = Float.parseFloat(value);
                break;
            case "double":
                object = Double.parseDouble(value);
                break;
            case "boolean":
                object = Boolean.parseBoolean(value);
                break;
            case "char":
                object = value;
                break;
            case "class java.lang.String":
                object = value;
                break;
            case "class java.lang.Byte":
                object = Byte.decode(value);
                break;
            case "class java.lang.Short":
                object = Short.decode(value);
                break;
            case "class java.lang.Integer":
                object = Integer.decode(value);
                break;
            case "class java.lang.Long":
                object = Long.decode(value);
                break;
            case "class java.lang.Float":
                object = Float.valueOf(value);
                break;
            case "class java.lang.Double":
                object = Double.valueOf(value);
                break;
            case "class java.lang.Boolean":
                object = Boolean.valueOf(value);
                break;
        }

        return object;
    }



}
